#include "EventLoop.hh"

using std::cout;
using std::endl;

EventLoop::EventLoop(Acceptor& acceptor)
    : _events(MAX_EVENTS)
    , _acceptor(acceptor){
    Create_epoll_fd();
    Add_epoll(_acceptor.Get_fd());
}
EventLoop::~EventLoop(){
    if(::close(_epoll_fd) == -1){
        error(1, errno, "close error");
    }
}
void EventLoop::Create_epoll_fd(){
    _epoll_fd = ::epoll_create(1);
    if(_epoll_fd == -1){
        error(1, errno, "epoll_create error");
    }
}
void EventLoop::Add_epoll(int fd){
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    int ret = ::epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &event);
}
void EventLoop::Del_epoll(int fd){

    int ret = ::epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
}
int EventLoop::Wait_epoll(int timeout){
    return ::epoll_wait(_epoll_fd, _events.data(), MAX_EVENTS, timeout);
}
void EventLoop::New_connection(){
    int new_fd = _acceptor.Accept();
    shared_ptr<TcpConnection> conn_ptr(new TcpConnection(new_fd));
    _connections.insert({new_fd, conn_ptr});
    Add_epoll(new_fd);

    conn_ptr->Set_new_conn(_new_conn);
    conn_ptr->Set_old_conn(_old_conn);
    conn_ptr->Set_close_conn(_close_conn);

    conn_ptr->New_conn();
}
void EventLoop::Old_connection(int fd){
    auto it =  _connections.at(fd);
    if(it->Is_close()){
        Del_epoll(fd);
        _connections.erase(fd);
        it->Close_conn();
    }else{
        it->Old_conn();
    }
}

void EventLoop::Loop(){
    while(true){
        _events.clear();
        int count = Wait_epoll(3000);   // 超时时间，单位为 ms
        if(count == 0){
            cout << "time out" << endl;
        }
        for(size_t i = 0; i < count; ++i){
            int current_fd = _events[i].data.fd;
            if (current_fd == _acceptor.Get_fd()){
                New_connection();
            }else{
                Old_connection(current_fd);
            }
        }

    }
}

void EventLoop::Set_new_conn(const fun_ptr& new_conn){
    _new_conn = new_conn;
}

void EventLoop::Set_old_conn(const fun_ptr& old_conn){
    _old_conn = old_conn;
}

void EventLoop::Set_close_conn(const fun_ptr& close_conn){
    _close_conn = close_conn;
}